/*
 * Based on 
 * JBox2D - A Java Port of Erin Catto's Box2D
 * 
 * JBox2D homepage: http://jbox2d.sourceforge.net/ 
 * Box2D homepage: http://www.box2d.org
 * 
 */


package pl.put.miasi.volleyball;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

import org.jbox2d.common.Mat22;
import org.jbox2d.common.Vec2;
import org.jbox2d.common.XForm;
import org.jbox2d.dynamics.DebugDraw;

import pl.put.miasi.agents.PlayerAgent;
import processing.core.PApplet;
import processing.core.PConstants;
import processing.core.PImage;

public class Main extends PApplet {

	private static final long serialVersionUID = 1712524774634907635L;

	//Volleyball match
	//protected AbstractVolleyball volleyball;
	public Volleyball volleyball;
	private boolean firstTime = true;
	private long startTime = System.currentTimeMillis();
	
	/** Is the options window open? */
	protected boolean handleOptions = false;
	
	// Little bit of input stuff
	/** Is the shift key held? */
	public boolean shiftKey = false;
    
	/** Was the mouse down last frame? */
    boolean pmousePressed = false;
    
    /** Our options handler - displays GUI and sets TestSettings for currentTest. */
    public Options options;
    
    // Processing handles fps pinning, but we
    // report fps on our own just to be sure.
    
    /** FPS that we want to achieve */
    final static float targetFPS = 20.0f;
    /** Number of frames to average over when computing real FPS */
    final int fpsAverageCount = 100; 
    /** Array of timings */
    long[] nanos;
    /** When we started the nanotimer */
    long nanoStart; //
    
    /** Number of frames since we started this example. */
    long frameCount = 0;
    
    /** Drawing handler to use. */
    public DebugDraw g;
    
    public String WALKING_WAV = "dzwieki\\walking.wav"; 
    public String RETURNING_WAV = "dzwieki\\returning.wav"; 
    public String NEWROUND_WAV = "dzwieki\\newround.wav"; 
    

	PImage backgroundImage = null;
	PImage frontImage = null;
	PImage endImage = null;
	PImage ballImage = null;
	List<SoundThread> thread = new ArrayList<SoundThread>();
	
	Implementation implementation;
	
    
    /** Constructor - real initialization happens in setup() function */
    public Main() {
    	super();
    }
    
    /**
     * Called once upon program initialization (by Processing).
     * Here we set up graphics, set the framerate, register
     * all the testbed examples, set up a mousewheel listener,
     * and set up the frame rate timer.
     */
    public void setup() {
    	/* On newer machines especially, the default JAVA2D renderer
    	 * is slow as hell and tends to drop frames.  I have no idea
    	 * why, but for now let's use P3D and live without the smoothing...
    	 */
    	
    	if (firstTime) {
        	size((int)Settings.WORLD_WIDTH_PX, (int)Settings.WORLD_HEIGHT_PX, PConstants.P3D);
        	frameRate(targetFPS);
        	g = new ProcessingDebugDraw(this);
        	//smooth();
        	for (int i=0; i<100; ++i) {
        		this.requestFocus();
        	}  
    		try {
    			frontImage = new PImage(ImageIO.read(new File("images/frontImage.gif")));

    		} catch (Exception e) {
    			e.printStackTrace();
    		}
        	
        	background(frontImage);
    	} else {
    		

    	

    	implementation = new Implementation(this);
    	registerVB(implementation);
    	implementation.initialize();

    	//Set up the mouse wheel listener to control zooming
/*    	addMouseWheelListener(new Mouse;WheelListener() {
            public void mouseWheelMoved(MouseWheelEvent e) {
            	if (volleyball != null) {
            		ProcessingDebugDraw d = (ProcessingDebugDraw)(volleyball.m_debugDraw);
            		int notches = e.getWheelRotation();
                	Vec2 oldCenter = d.screenToWorld(width / 2.0f, height / 2.0f);
                	//Change the zoom and clamp it to reasonable values 
                	if (notches < 0) {
                		d.scaleFactor = min(300f, d.scaleFactor * 1.05f);
                	}
                	else if (notches > 0) {
                		d.scaleFactor = max(.02f, d.scaleFactor / 1.05f);
                	}
                	Vec2 newCenter = d.screenToWorld(width / 2.0f, height / 2.0f);
                	d.transX -= (oldCenter.x - newCenter.x) * d.scaleFactor;
                	d.transY -= (oldCenter.y - newCenter.y) * d.scaleFactor;
                	volleyball.cachedCamScale = d.scaleFactor;
            	}
            }
        });*/
    	
    	
    	/* Set up the timers for FPS reporting */
    	nanos = new long[fpsAverageCount];
    	long nanosPerFrameGuess = (long)(1000000000.0 / targetFPS);
    	nanos[fpsAverageCount-1] = System.nanoTime();
    	for (int i=fpsAverageCount-2; i>=0; --i) {
    		nanos[i] = nanos[i+1] - nanosPerFrameGuess;
    	}
    	nanoStart = System.nanoTime();
    	
    	options = new Options(this);
    	
    	
    	

		try {
			backgroundImage = new PImage(ImageIO.read(new File("images/background.gif")));

			background(backgroundImage);
		} catch (Exception e) {
			e.printStackTrace();
		}
		

		
		try {
			endImage = new PImage(ImageIO.read(new File("images/endImage.gif")));

		} catch (Exception e) {
			e.printStackTrace();
		}
    	}
    }
    
    /**
     * This is the main looping function, and is called targetFPS times per second.
     * In the testbed, Processing takes care of the timing of these calls for us,
     * but in your own game you will likely need to handle that yourself.  This function
     * also keeps detailed track of the current FPS and Vec2 creations for optimization
     * purposes.
     */
    public void draw() {

    	
    	if (System.currentTimeMillis() - startTime < Settings.LOADING) {
    		background(this.frontImage);
    		return;
    	}
    	if (firstTime) {
    		firstTime = false;
    		setup();
    	}
    	
    	if (handleOptions) {
    		options.handleOptions();
    	} else if (volleyball.endOfMatch) {
    		background(this.endImage);
    		if (volleyball.settings.drawStats) {
    			g.drawString(Settings.RESULT_X, Settings.RESULT_Y, "Result: " + volleyball.blueResult + ":" + volleyball.yellowResult, Volleyball.white);
    			if (volleyball.blueResult > volleyball.yellowResult)
    				g.drawString(Settings.END_X, Settings.END_Y, "End of match: Blue team won!", Volleyball.blue);
    			else
    				g.drawString(Settings.END_X, Settings.END_Y, "End of match: Yellow team won!", Volleyball.yellow);
    		}
    	} else{
    		background(this.backgroundImage);
    		Vec2.creationCount = 0;

    		/* Make sure we've got a valid test to run and reset it if needed */
    		if (volleyball == null) {
    			nanoStart = System.nanoTime();
    			frameCount = 0;
    		}
    		if (volleyball.needsReset) {
    			//System.out.println("Resetting "+currentTest.getName());
    			Settings s = volleyball.settings; //copy settings
    			volleyball.initialize();
    			if (s != null) volleyball.settings = s;
    			nanoStart = System.nanoTime();
    			frameCount = 0;
    		}
    		/*volleyball.m_textLine = AbstractVolleyball.textLineHeight;
    		g.drawString(5, volleyball.m_textLine, volleyball.getName(),AbstractVolleyball.white);
    		volleyball.m_textLine += 2*AbstractVolleyball.textLineHeight;*/

    		/* Take our time step (drawing is done here, too) */
    		volleyball.step();

    		/* If the user wants to move the canvas, do it */
    		handleCanvasDrag();

    		if (volleyball.settings.drawStats) {
    			g.drawString(Settings.RESULT_X, Settings.RESULT_Y, "Result: " + volleyball.blueResult + ":" + volleyball.yellowResult, Volleyball.white);
    		}

    		/* ==== Vec2 creation and FPS reporting ==== 
    		if (volleyball.settings.drawStats) {
    			g.drawString(5, volleyball.m_textLine, "Vec2 creations/frame: "+Vec2.creationCount, AbstractVolleyball.white);
    			volleyball.m_textLine += AbstractVolleyball.textLineHeight;
    		}

    		for (int i=0; i<fpsAverageCount-1; ++i) {
    			nanos[i] = nanos[i+1];
    		}
    		nanos[fpsAverageCount-1] = System.nanoTime();
    		float averagedFPS = (float) ( (fpsAverageCount-1) * 1000000000.0 / (nanos[fpsAverageCount-1]-nanos[0]));
    		++frameCount;
    		float totalFPS = (float) (frameCount * 1000000000 / (1.0*(System.nanoTime()-nanoStart)));        
    		if (volleyball.settings.drawStats) {
    			g.drawString(5, volleyball.m_textLine, "Average FPS ("+fpsAverageCount+" frames): "+averagedFPS, AbstractVolleyball.white);
    			volleyball.m_textLine += AbstractVolleyball.textLineHeight;
    			g.drawString(5, volleyball.m_textLine, "Average FPS (entire test): "+totalFPS, AbstractVolleyball.white);
    			volleyball.m_textLine += AbstractVolleyball.textLineHeight;
    		}*/
    	}

		/* Store whether the mouse was pressed this step */
		pmousePressed = mousePressed;
    }
    
    /**
     * Allows the world to be dragged with a right-click.
     */
    public void handleCanvasDrag() {
    	//Handle mouse dragging stuff
        //Left mouse attaches mouse joint to object.
        //Right mouse drags canvas.
    	ProcessingDebugDraw d = (ProcessingDebugDraw)(volleyball.m_debugDraw);
		
        //Vec2 mouseWorld = d.screenToWorld(mouseX, mouseY);
        if (mouseButton == RIGHT) {
            if (mousePressed) {
                d.transX += mouseX - pmouseX;
                d.transY -= mouseY - pmouseY;
                Vec2 v = d.screenToWorld(width*.5f,height*.5f);
                volleyball.cachedCamX = v.x;
                volleyball.cachedCamY = v.y;
            }
        }
    
    }
    
    /** Dispatch mousePressed events to the current test. */
    public void mousePressed() {
    	if (volleyball == null || handleOptions) return;
    	volleyball.mouseDown(new Vec2(mouseX,mouseY));
    }
    
    /** Dispatch mouseReleased events to the current test. */
    public void mouseReleased() {
    	if (volleyball == null || handleOptions) return;
    	volleyball.mouseUp();
    }
    
    /** Dispatch mouseMoved events to the current test. */
    public void mouseMoved() {
    	if (volleyball == null || handleOptions) return;
    	volleyball.mouseMove(new Vec2(mouseX,mouseY));
    }
    
    /** Dispatch mouseDragged events to the current test. */
    public void mouseDragged() {
    	mouseMoved();
    }
    
    /**
     * Apply keyboard shortcuts, do keypress handling, and then
     * send the key event to the current test if appropriate.
     */
    public void keyPressed() {
    	if (key=='o') {
    		handleOptions = !handleOptions;
    		if (handleOptions) options.initialize(volleyball);
    	}
    	
    	if (keyCode == PApplet.SHIFT) {
            shiftKey = true;
        }
    	if (handleOptions) return;
    	
    	/*
    	if (keyCode == PApplet.RIGHT) {
    		++currentTestIndex;
    		if (currentTestIndex >= volleyballs.size()) currentTestIndex = 0;
    		//System.out.println(currentTestIndex);
    		currentTest = volleyballs.get(currentTestIndex);
    		currentTest.needsReset = true;
    		return;
    	} else if (keyCode == PApplet.LEFT) {
    		--currentTestIndex;
    		if (currentTestIndex < 0) currentTestIndex = volleyballs.size()-1;
    		//System.out.println(currentTestIndex);
    		currentTest = volleyballs.get(currentTestIndex);
    		currentTest.needsReset = true;
    		return;
    	}
    	*/
    	if (volleyball == null) 
    		return; 

    	if (key == ' ') 
    		volleyball.launchBomb();
    	if (key == 'p') {
    		volleyball.settings.pause = !volleyball.settings.pause;
    	}
    	if (key == '+' && volleyball.settings.pause) {
        	volleyball.settings.singleStep = true;
        }
    	if (key == 's') 
    		volleyball.settings.drawStats = !volleyball.settings.drawStats;
    	if (key == 'c') 
    		volleyball.settings.drawContactPoints = !volleyball.settings.drawContactPoints;
    	if (key == 'b') 
    		volleyball.settings.drawAABBs = !volleyball.settings.drawAABBs;
    
    	/********/
    	
    	
    	if (key == 'd' || key == 'f')
    	{
    		
    		//PlayerAgent agent = new PlayerAgent(number, team, gui)
    		movePlayer(0, key);	
    	}
    	
    	if (key == 'e' || key == 'r')
    	{
    		moveRacket(0, key);
    	}
    	   	
    	/*else if (key == 'r')
    	{    		
    		System.out.println("ball.x: "+volleyball.ballBody.getPosition().x+", "+volleyball.ballBody.getPosition().y);
    		//playSound(NEWROUND_WAV);
    		//volleyball.needsReset = true;
    	}
    	/********/


    		
    	volleyball.keyPressed(key);
    }
    
    /** Handle keyReleased events and pass them on to currentTest. */
    public void keyReleased() {
    	if (keyCode == PApplet.SHIFT) {
            shiftKey = false;
        }
    	if (volleyball == null) return;
    	volleyball.keyReleased(key);
    }
    
    /** Register an AbstractExample to the current list of examples. */
    public void registerVB(Volleyball vb) {
    	volleyball = vb;
    }
    
	public void movePlayer(int which, char move) {

		//Vec2 center = implementation.b2Player[which].corpsBody.getWorldCenter();
		Vec2 velocity = new Vec2(1.0f, 0.0f);
		//implementation.b2Player[which].moving = false;
		switch (move) {
		case 'd':
			//velocity = center.mul(+2.0f);
			velocity = velocity.mul(1.0f);
			implementation.b2Player[which].movePlayer(velocity);
			implementation.b2Player[which].moving = true;
			break;
		case 'f':
			//velocity = center.mul(-2.0f);
			velocity = velocity.mul(-1.0f);
			implementation.b2Player[which].movePlayer(velocity);
			implementation.b2Player[which].moving = true;
			break;
		}
	}
	
	public void movePlayer(int which, Vec2 velocity) {
		implementation.b2Player[which].movePlayer(velocity);
		implementation.b2Player[which].moving = true;

	}
	
	public void moveRacket(int which, char move) {
		//Vec2 center = implementation.b2Player[which].corpsBody.getWorldCenter();
		
		float angVelocity = 1.0f;
		//implementation.b2Player[which].moving = false;
		switch (move) {
		case 'e':
			angVelocity *= 1.0f;
			implementation.b2Player[which].moveRacket(angVelocity);
			implementation.b2Player[which].moving = true;
			break;
		case 'r':
			angVelocity *= -1.0f;
			implementation.b2Player[which].moveRacket(angVelocity);
			implementation.b2Player[which].moving = true;
			break;
		}
	}
	
	public void moveRacket(int which, float velocity) {
		//Vec2 center = implementation.b2Player[which].corpsBody.getWorldCenter();
		implementation.b2Player[which].moveRacket(velocity);
		implementation.b2Player[which].moving = true;
	}
   
    /** Start PApplet as a Java program (can also be run as an applet). */
    static public void main(String args[]) {
        PApplet.main(new String[] { "pl.put.miasi.volleyball.Main" });
    }


}
